(setq custom-file (concat user-emacs-directory "custom.el"))
(when (file-exists-p custom-file)
  (load custom-file))

(setq inhibit-startup-message t)

(scroll-bar-mode -1)        ; Disable visible scrollbar
(tool-bar-mode -1)          ; Disable the toolbar
(tooltip-mode -1)           ; Disable tooltips
(set-fringe-mode 0)         ; Disable fringes
(menu-bar-mode -1)          ; Disagel menubar

(setq visual-bell t)

(column-number-mode)

(setq display-line-numbers-type 'relative) ;; Display relative line numbers
(add-hook 'prog-mode-hook #'display-line-numbers-mode) ;; Enable line numbers only when programming

(defvar my/default-font "JetBrains Mono")
(defvar my/default-font-size 130)
(defvar my/default-variable-font-size 120)

(set-face-attribute 'default        nil :font  my/default-font   :height my/default-font-size)
(set-face-attribute 'fixed-pitch    nil :font  my/default-font   :height my/default-font-size)
(set-face-attribute 'variable-pitch nil :font "DejaVu Sans"  :height my/default-variable-font-size)

(require 'package)
(when (version< emacs-version "28")
  (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(add-to-list 'package-archives '("stable" . "https://stable.melpa.org/packages/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

(customize-set-variable 'package-archive-priorities
			'(("gnu"    . 99)   ; prefer GNU packages
			  ("nongnu" . 80)   ; use non-gnu packages if
					; not found in GNU elpa
			  ("stable" . 70)   ; prefer "released" versions
					; from elpa
			  ("melpa"  . 0)))  ; if all else fails, get it
					; from melpa
(package-initialize)

(defun toggle-transparency ()
  (interactive)
  (let ((alpha (frame-parameter nil 'alpha)))
    (set-frame-parameter
     nil 'alpha
     (if (eql (cond ((numberp alpha) alpha)
                    ((numberp (cdr alpha)) (cdr alpha))
                    ;; Also handle undocumented (<active> <inactive>) form.
                    ((numberp (cadr alpha)) (cadr alpha)))
              100)
         '(85 . 50) '(100 . 100)))))
(global-set-key (kbd "C-c t") 'toggle-transparency)

(defadvice find-file (before make-directory-maybe (filename &optional wildcards) activate)
  "Create parent directory if not exists while visiting file."
  (unless (file-exists-p filename)
    (let ((dir (file-name-directory filename)))
      (unless (file-exists-p dir)
        (make-directory dir t)))))

(defun my/create-dir-if-not-exists (dir)
  (when (not (file-exists-p dir))
    (make-directory dir)))

;; Chanage autosaves directory
(defvar auto-save-directory (expand-file-name "autosaves/" user-emacs-directory))
(my/create-dir-if-not-exists auto-save-directory)
(setq auto-save-file-name-transforms
      `((".*" ,auto-save-directory t)))

;; Change backup dir
(setq backup-by-copying t)
(defvar backup-directory (expand-file-name "backups/" user-emacs-directory))
(my/create-dir-if-not-exists backup-directory)
(setq backup-directory-alist
      `(("." . ,backup-directory)))

;; Change lockfile directory
(defvar lock-file-directory (expand-file-name "lockfiles/" user-emacs-directory))
(my/create-dir-if-not-exists lock-file-directory)
(setq lock-file-name-transforms
      `((".*" ,lock-file-directory t)))

(customize-set-variable
 'tramp-backup-directory-alist backup-directory-alist)

(set-language-environment 'Russian)

(global-set-key (kbd "C-c w h") 'windmove-left)
(global-set-key (kbd "C-c w l") 'windmove-right)
(global-set-key (kbd "C-c w k") 'windmove-up)
(global-set-key (kbd "C-c w j") 'windmove-down)
(global-set-key (kbd "C-c w J") 'split-window-below)
(global-set-key (kbd "C-c w L") 'split-window-right)
(global-set-key (kbd "C-c w d") 'delete-window)

(global-set-key (kbd "C-c b b") 'switch-to-buffer)
(global-set-key (kbd "C-c b j") 'next-buffer)
(global-set-key (kbd "C-c b k") 'previous-buffer)

(electric-pair-mode 1)
(electric-indent-mode 1)

;; No need to put :ensure t
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))
  (require 'use-package)
  (setq use-package-always-ensure t)
;;  (setq use-package-always-defer t)

(use-package gnu-elpa-keyring-update)

(defun meow-setup ()
  (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
  (meow-motion-overwrite-define-key
   '("j" . meow-next)
   '("k" . meow-prev)
   '("<escape>" . ignore))
  (meow-leader-define-key
   ;; SPC j/k will run the original command in MOTION state.
   '("j" . "H-j")
   '("k" . "H-k")
   ;; Use SPC (0-9) for digit arguments.
   '("1" . meow-digit-argument)
   '("2" . meow-digit-argument)
   '("3" . meow-digit-argument)
   '("4" . meow-digit-argument)
   '("5" . meow-digit-argument)
   '("6" . meow-digit-argument)
   '("7" . meow-digit-argument)
   '("8" . meow-digit-argument)
   '("9" . meow-digit-argument)
   '("0" . meow-digit-argument)
   '("/" . meow-keypad-describe-key)
   '("?" . meow-cheatsheet))
  (meow-normal-define-key
   '("0" . meow-expand-0)
   '("9" . meow-expand-9)
   '("8" . meow-expand-8)
   '("7" . meow-expand-7)
   '("6" . meow-expand-6)
   '("5" . meow-expand-5)
   '("4" . meow-expand-4)
   '("3" . meow-expand-3)
   '("2" . meow-expand-2)
   '("1" . meow-expand-1)
   '("-" . negative-argument)
   '(";" . meow-reverse)
   '("," . meow-inner-of-thing)
   '("." . meow-bounds-of-thing)
   '("[" . meow-beginning-of-thing)
   '("]" . meow-end-of-thing)
   '("a" . meow-append)
   '("A" . meow-open-below)
   '("b" . meow-back-word)
   '("B" . meow-back-symbol)
   '("c" . meow-change)
   '("d" . meow-delete)
   '("D" . meow-backward-delete)
   '("e" . meow-next-word)
   '("E" . meow-next-symbol)
   '("f" . meow-find)
   '("g" . meow-cancel-selection)
   '("G" . meow-grab)
   '("h" . meow-left)
   '("H" . meow-left-expand)
   '("i" . meow-insert)
   '("I" . meow-open-above)
   '("j" . meow-next)
   '("J" . meow-next-expand)
   '("k" . meow-prev)
   '("K" . meow-prev-expand)
   '("l" . meow-right)
   '("L" . meow-right-expand)
   '("m" . meow-join)
   '("n" . meow-search)
   '("o" . meow-block)
   '("O" . meow-to-block)
   '("p" . meow-yank)
   '("q" . meow-quit)
   '("Q" . meow-goto-line)
   '("r" . meow-replace)
   '("R" . meow-swap-grab)
   '("s" . meow-kill)
   '("t" . meow-till)
   '("u" . meow-undo)
   '("U" . meow-undo-in-selection)
   '("v" . meow-visit)
   '("w" . meow-mark-word)
   '("W" . meow-mark-symbol)
   '("x" . meow-line)
   '("X" . meow-goto-line)
   '("y" . meow-save)
   '("Y" . meow-sync-grab)
   '("z" . meow-pop-selection)
   '("'" . repeat)
   '("<escape>" . ignore)))

(use-package meow
  :config
  (setq meow-replace-state-name-list
        '((normal . "[N]")
          (motion . "[M]")
          (keypad . "[K]")
          (insert . "[I]")
          (beacon . "[B]")))
  (meow-setup)
  (meow-global-mode 1))

(use-package doom-themes
  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  (load-theme 'doom-monokai-classic t)

  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
  ;; Enable custom neotree theme (all-the-icons must be installed!)
  ;;(doom-themes-neotree-config)
  ;; or for treemacs users
  (setq doom-themes-treemacs-theme "doom-colors") ; use "doom-colors" for less minimal icon theme
  (doom-themes-treemacs-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

(use-package all-the-icons)

(use-package doom-modeline
  :init (doom-modeline-mode 1))

(use-package highlight-numbers
  :hook (prog-mode . highlight-numbers-mode))

(use-package ligature
  :config
  ;; Enable all JetBrains Mono ligatures in programming modes
  (ligature-set-ligatures 'prog-mode '("-|" "-~" "---" "-<<" "-<" "--" "->" "->>" "-->" "///" "/=" "/=="
                                      "/>" "//" "/*" "*>" "***" "*/" "<-" "<<-" "<=>" "<=" "<|" "<||"
                                      "<|||" "<|>" "<:" "<>" "<-<" "<<<" "<==" "<<=" "<=<" "<==>" "<-|"
                                      "<<" "<~>" "<=|" "<~~" "<~" "<$>" "<$" "<+>" "<+" "</>" "</" "<*"
                                      "<*>" "<->" "<!--" ":>" ":<" ":::" "::" ":?" ":?>" ":=" "::=" "=>>"
                                      "==>" "=/=" "=!=" "=>" "===" "=:=" "==" "!==" "!!" "!=" ">]" ">:"
                                      ">>-" ">>=" ">=>" ">>>" ">-" ">=" "&&&" "&&" "|||>" "||>" "|>" "|]"
                                      "|}" "|=>" "|->" "|=" "||-" "|-" "||=" "||" ".." ".?" ".=" ".-" "..<"
                                      "..." "+++" "+>" "++" "[||]" "[<" "[|" "{|" "??" "?." "?=" "?:" "##"
                                      "###" "####" "#[" "#{" "#=" "#!" "#:" "#_(" "#_" "#?" "#(" ";;" "_|_"
                                      "__" "~~" "~~>" "~>" "~-" "~@" "$>" "^=" "]#"))
  ;; Enables ligature checks globally in all buffers. You can also do it
  ;; per mode with `ligature-mode'.
  (global-ligature-mode t))

(use-package org-faces
  :ensure nil
  :after org
  :custom-face
  (org-block           ((nil (:inherit fixed-pitch :foreground nil))))
  (org-code            ((nil (:inherit (shadow fixed-pitch)))))
  (org-table           ((nil (:inherit fixed-pitch))))
  (org-verbatim        ((nil (:inherit (shadow fixed-pitch)))))
  (org-special-keyword ((nil (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-meta-line       ((nil (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-checkbox        ((nil (:inherit fixed-pitch)))))

(defun my/org-mode-setup ()
  (org-indent-mode)
  (org-hide-block-all) ;; Collapse all blocks
  (variable-pitch-mode 1)
  (visual-line-mode 1))

(use-package org
  :hook (org-mode . my/org-mode-setup))

(use-package org-superstar
  :after org
  :hook (org-mode . (lambda () (org-superstar-mode 1))))

(defun my/org-mode-visual-fill ()
  (setq visual-fill-column-width 120
        visual-fill-column-center-text t)
  (visual-fill-column-mode 1))

(use-package visual-fill-column
  :after org
  :hook (org-mode . my/org-mode-visual-fill))

(use-package corfu
  :custom
  (corfu-auto t)
  (corfu-separator ?\s)
  (corfu-quit-no-match 'separator)
  :init
  (global-corfu-mode)
  :bind (:map corfu-map	([remap isearch-forward] . corfu-insert-separator)))

(use-package emacs
  :init
  (setq tab-always-indent 'complete))

(use-package corfu-popupinfo
  :ensure nil
  :after corfu
  :custom
  (corfu-popupinfo-delay 0)
  :init
  (corfu-popupinfo-mode)
  :bind (:map corfu-popupinfo-map
        ("M-p" . corfu-popupinfo-scroll-down)
        ("M-n" . corfu-popupinfo-scroll-up)))

(use-package kind-icon
:ensure t
:after corfu
:custom
(kind-icon-use-icons nil)
(kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

(use-package orderless
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles basic partial-completion))
                                   (eglot (styles orderless)))))

(use-package cape
  :init
  (dolist (capf (list #'cape-dabbrev
                      #'cape-file))
    (add-to-list 'completion-at-point-functions capf)))

(use-package yasnippet
  :diminish
  :defer t
  :config
  (yas-reload-all)
  (yas-global-mode))

(use-package yasnippet-snippets)

(use-package company)

(use-package company-yasnippet
  :ensure nil
  :config
  (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-yasnippet)))

(use-package vertico
  :init (vertico-mode))

;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :init
  (savehist-mode))

;; A few more useful configurations...
(use-package emacs
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (car args))
          (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

  ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
  ;; Vertico commands are hidden in normal buffers.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)

  ;; Enable recursive minibuffers
  (setq enable-recursive-minibuffers t))

(use-package vertico-directory
  :after vertico
  :ensure nil
  :bind (:map vertico-map
              ("RET" . vertico-directory-enter)
              ("DEL" . vertico-directory-delete-char)
              ("M-DEL" . vertico-directory-delete-word))
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

(use-package marginalia
  ;; Either bind `marginalia-cycle' globally or only in the minibuffer
  :bind (("M-A" . marginalia-cycle)
         :map minibuffer-local-map
         ("M-A" . marginalia-cycle))

  ;; The :init configuration is always executed (Not lazy!)
  :init

  ;; Must be in the :init section of use-package such that the mode gets
  ;; enabled right away. Note that this forces loading the package.
  (marginalia-mode))

(use-package consult
  :bind
  (([remap switch-to-buffer] . consult-buffer)
   ([remap load-theme] . consult-themes)
   ("M-s l" . consult-line)))

(use-package flycheck
  :init (global-flycheck-mode))

(use-package lsp-mode
  :custom
  (lsp-completion-provider :none)
  :init
  (setq lsp-keymap-prefix "C-c l")
  (meow-leader-define-key
   `("l" ,lsp-keymap-prefix))

  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
          '(orderless));; Configure flex
    (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point)
                                                    (cape-company-to-capf #'company-yasnippet)
                                                    (cape-dabbrev)
                                                    (cape-file))))

  :config
  ;; Uncomment following section if you would like to tune lsp-mode performance according to
  ;; https://emacs-lsp.github.io/lsp-mode/page/performance/
  (setq gc-cons-threshold 100000000) ;; 100mb
  (setq read-process-output-max (* 1024 1024)) ;; 1mb
  (setq lsp-idle-delay 0.500)
  (setq lsp-log-io nil)
  (setq lsp-prefer-flymake nil)
  ;; Makes LSP shutdown the metals server when all buffers in the project are closed.
  ;; https://emacs-lsp.github.io/lsp-mode/page/settings/mode/#lsp-keep-workspace-alive
  (setq lsp-keep-workspace-alive nil)
  :hook
  (lsp-completion-mode . my/lsp-mode-setup-completion))

(use-package treemacs)
(use-package lsp-treemacs)

(use-package lsp-ui)
;; Posframe is a pop-up tool that must be manually installed for dap-mode
(use-package posframe)

;; Use the Debug Adapter Protocol for running tests and debugging
(use-package dap-mode
  :hook
  (lsp-mode . dap-mode)
  (lsp-mode . dap-ui-mode))

(use-package consult-lsp)

(use-package lsp-metals
  :after lsp-mode)
(add-hook 'scala-mode-hook #'lsp-deferred)

(use-package lsp-pyright
  :hook (python-mode . (lambda ()
                          (require 'lsp-pyright)
                          (lsp-deferred))))  ; or lsp-deferred

(use-package magit)

(use-package helpful
  :bind
  (([remap describe-function] . helpful-callable)
   ([remap describe-variable] . helpful-variable)
   ([remap describe-key]      . helpful-key)
   ([remap describe-command]  . helpful-command)))

(use-package fish-mode)

(use-package haskell-mode)

(use-package clojure-mode)

(use-package cider)

(defun my/clojure-mode-hook ()
    (clj-refactor-mode 1)
    (yas-minor-mode 1) ; for adding require/use/import statements
    ;; This choice of keybinding leaves cider-macroexpand-1 unbound
    (cljr-add-keybindings-with-prefix "C-c C-m"))
(use-package clj-refactor
  :hook (clojure-mode . my/clojure-mode-hook))

(use-package csharp-mode)

(use-package scala-mode
  :interpreter ("scala" . scala-mode))

;; Enable sbt mode for executing sbt commands
(use-package sbt-mode
  :commands sbt-start sbt-command
  :config
  ;; WORKAROUND: https://github.com/ensime/emacs-sbt-mode/issues/31
  ;; allows using SPACE when in the minibuffer
  (substitute-key-definition
   'minibuffer-complete-word
   'self-insert-command
   minibuffer-local-completion-map)
   ;; sbt-supershell kills sbt-mode:  https://github.com/hvesalai/emacs-sbt-mode/issues/152
   (setq sbt:program-options '("-Dsbt.supershell=false")))

(use-package rustic)

(defun my/puni-meow-keymap ()
  ;; Modify normal keymap
  (meow-normal-define-key
   '("("   . puni-wrap-round)
   '("{"   . puni-wrap-curly)
   '("<"   . puni-wrap-angle)
   '("M-[" . puni-wrap-square))

  ;; Add leader to call from <spc> p
  (meow-leader-define-key
   (cons "p" puni-mode-map))
)
(use-package puni
 :after meow
 :init
 (dolist (hook '(prog-mode-hook sgml-mode-hook nxml-mode-hook tex-mode-hook eval-expression-minibuffer-setup-hook))
   (add-hook hook #'puni-mode))
 (my/puni-meow-keymap)

 :bind (:map puni-mode-map
             ("C-<right>" . puni-slurp-forward)
             ("C-<left>"  . puni-barf-forward)
             ("C-S-<left>" . puni-slurp-backward)
             ("C-S-<right>" . puni-barf-backward)
             ("C-c C-p s" . puni-splice)
             ("C-c C-p q" . puni-squeeze)
             ("C-c C-p c" . puni-convolute)
             ("C-c C-p t" . puni-transpose)
             ("C-c C-p |" . puni-split)))

(use-package telega)

(use-package vterm)

(use-package path-helper
  :if (memq window-system '(mac ns))
  :ensure t
  :config
  (path-helper-setenv-all))

(use-package eshell-prompt-extras
  :config
  (with-eval-after-load "esh-opt"
  (autoload 'epe-theme-lambda "eshell-prompt-extras")
  (setq eshell-highlight-prompt nil
        eshell-prompt-function 'epe-theme-lambda)))
